package org.springframework.aop.framework;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

import java.lang.reflect.AccessibleObject;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;

/**
 * @Name ReflectiveMethodInvocation
 * @Description: 调用目标方法的 [实现] 类
 * 描述信息：用于在代理方法中执行目标方法。
 * @Version v1.0.0
 * @Author: lichao
 * @Date: 2024-03-27 15:35:14
 **/
public class ReflectiveMethodInvocation implements MethodInvocation {
    // 代理对象
    protected final Object proxy;
    // 目标对象
    protected final Object target;
    // 目标方法
    protected final Method method;
    // 目标方法入参
    protected final Object[] arguments;
    // 目标类
    protected final Class<?> targetClass;
    // 拦截器链
    protected final List<Object> interceptorsAndDynamicMethodMatches;
    private int currentInterceptorIndex = -1;

    public ReflectiveMethodInvocation(Object proxy, Object target, Method method, Object[] arguments, Class<?> targetClass, List<Object> interceptorsAndDynamicMethodMatches) {
        this.proxy = proxy;
        this.target = target;
        this.method = method;
        this.arguments = arguments;
        this.targetClass = targetClass;
        this.interceptorsAndDynamicMethodMatches = interceptorsAndDynamicMethodMatches;
    }

    @Override
    public Object proceed() throws Throwable {
        // [多切面增强]：保证多个切面同时匹配同一方法的而不出现乱序的实现原理。
        // 0. 初始化 currentInterceptorIndex 为 -1，每调用一次 proceed 方法就把 currentInterceptorIndex + 1
        // 2. 当调用次数 = 拦截器个数时，执行目标方法
        if (currentInterceptorIndex == interceptorsAndDynamicMethodMatches.size() - 1) {
            return method.invoke(target, arguments);
        }
        Object interceptorOrInterceptionAdvice = interceptorsAndDynamicMethodMatches.get(++currentInterceptorIndex);
        // 1. 当调用次数 < 拦截器个数时，执行拦截（代理）方法
        return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
    }

    @Override
    public Method getMethod() {
        return method;
    }

    @Override
    public Object[] getArguments() {
        return arguments;
    }

    @Override
    public Object getThis() {
        return target;
    }

    @Override
    public AccessibleObject getStaticPart() {
        return method;
    }
}

